#define _GNU_SOURCE
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include <utils.h>
#include <surface.h>
#include <debug.h>

/**
 * Create a new surface with the given dimension.
 *
 * @param width surface horizontal dimension
 * @param height surface vertical dimension
 *
 * @return new allocated surface.
 */
surface_t *
surface_new( uint16_t width, uint16_t height )
{
  surface_t *s;

  s = calloc( 1, sizeof(*s) );

  if ( s != NULL )
    {
      s->size.x = width;
      s->size.y = height;

      s->buffer = calloc( width * height, sizeof(*(s->buffer)) );

      if ( s->buffer == NULL )
	{
	  free( s );
	  s = NULL;
	  err( "Could not allocate memory for surface!" );
	}
    }

  return s;
}


/**
 * Free surface.
 *
 * @param surface pointer to surface, must be != NULL.
 */
void
surface_free( surface_t *surface )
{
  assert( surface != NULL );
  if ( surface->buffer != NULL )
    free( surface->buffer );
  free( surface );
}


/**
 * Clear (blanks) surface.
 *
 * @param surface pointer to surface, must be != NULL.
 */
void
surface_clear( const surface_t *surface )
{
  assert( surface != NULL );
  assert( surface->buffer != NULL );
  memset( surface->buffer, 0,
	  ( surface->size.x * surface->size.y ) * sizeof(color_t) );
}


/*
 * HACK:
 * We will use the still unused color_t.a field to set visited elements.
 */

void
surface_reset_visits( const surface_t *surface )
{
  assert( surface != NULL );
  assert( surface->buffer != NULL );

  color_t *p, *end = surface->buffer + ( surface->size.x * surface->size.y );
  for ( p=surface->buffer; p < end; p++ )
    p->a = 0;
}

void
surface_fill( const surface_t *surface, color_t color, rect_t rect )
{
  assert( surface != NULL );

  if ( rect.p0.x > rect.p1.x )
    swap( rect.p0.x, rect.p1.x );

  if ( rect.p0.y > rect.p1.y )
    swap( rect.p0.y, rect.p1.y );

  rect.p1.x = min( rect.p1.x, (uint16_t)(surface->size.x - 1 ) );
  rect.p1.y = min( rect.p1.y, (uint16_t)(surface->size.y - 1 ) );

  uint16_t w = surface->size.x;
  color_t *start=&PIXEL_AT( surface, rect.p0.x, rect.p0.y );
  color_t *end=&PIXEL_AT( surface, rect.p1.x, rect.p1.y );

  uint16_t lw = rect.p1.x - rect.p0.x;

  color_t *p, *l, *lend;
  for ( p=start; p <= end; p += w )
    {
      lend = p + lw;
      for ( l=p; l <= lend; l ++ )
	*l = color;
    }
}
